package com.baturu.vin.ford.service.impl;

import com.baturu.vin.base.service.impl.EPCAnalyser;
import com.baturu.vin.dto.*;
import com.baturu.vin.ford.constants.V2Constant;
import com.baturu.vin.ford.dal.dao.*;
import com.baturu.vin.ford.dal.entity.FordPartsMappingEntity;
import com.baturu.vin.ford.dal.entity.FordV2PartsInfoEntity;
import com.baturu.vin.ford.dto.*;
import com.baturu.vin.kit.StreamKit;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 福特单机版
 *
 * @Author: chenjiahao
 * @Time: 2019/1/16
 */
@Service("fordV2EPCAnalyser")
@Slf4j
public class FordV2EPCAnalyser extends EPCAnalyser<FordV2VinAnalyserResultDTO, FordPartsMappingEntity, FordV2PartsInfoEntity, EPCCarTypeDTO> implements InitializingBean {

    @Autowired
    private FordVinInfoDAO vinInfoDAO;

    @Autowired
    private FordPartsMappingDAO partsMappingDAO;

    @Autowired
    private FordV2GroupDAO groupDAO;

    @Autowired
    private FordCarTypeDAO carTypeDAO;

    @Autowired
    private FordCommonDAO commonDAO;

    @Autowired
    private FordV2PartsInfoDAO fordV2PartsInfoDAO;

    @Autowired
    private FordBtrPartsDAO fordBtrPartsDAO;

    private final static String DATE_REX = "[DBEN ]";

    private final Pattern isNum = Pattern.compile("^[-\\+]?[\\d]*$");
    private final Pattern areaPattern = Pattern.compile("\"[/a-zA-Z]+\"");
    private final Pattern conditionPattern = Pattern.compile("[ ~]");

    private Map<String, String> ignoreCarSystem;

    private Map<String, String> btrPartsNameRule;

    @Override
    protected FordV2VinAnalyserResultDTO analyseVinCode(String vinCode) {

        List<FordV2VinAnalyserResultDTO> vinAnalyserResultDTOList = vinInfoDAO.queryV2ByVinCode(vinCode);
        if (vinAnalyserResultDTOList.size() != 1) {
            log.info("福特V2没有此vin信息或者vin有异常:{}", vinCode);
            return null;
        }
        FordV2VinAnalyserResultDTO vinAnalyserResultDTO = vinAnalyserResultDTOList.get(0);
        String country = vinInfoDAO.queryCountryByPreThree(vinCode.substring(0, 3));
        if (StringUtils.isBlank(country)) {
            log.info("福特V2没有此vin国家信息，锁定不了车型:{}", vinCode);
            return null;
        }
        CarTypeDetail carTypeDetail = vinInfoDAO.queryV2CarTypeId(country, vinAnalyserResultDTO.getDate(), vinAnalyserResultDTO.getCarType());
        if (Objects.isNull(carTypeDetail)) {
            log.info("福特V2没有此vin车型索引，锁定不了车型:{}", vinCode);
            return null;
        }
        if (invaildCarsystem(vinAnalyserResultDTO, carTypeDetail)) {
            return null;
        }

        vinAnalyserResultDTO.setCarTypeDetail(carTypeDetail);
        vinAnalyserResultDTO.setCountry(country);

        String engine = vinInfoDAO.queryCarTypeEngine(vinAnalyserResultDTO.getCarType());
        if (StringUtils.isNotBlank(engine)) {
            vinAnalyserResultDTO.setEngineDetail(engine);
        }else {
            vinAnalyserResultDTO.setEngineDetail("");
        }

        return vinAnalyserResultDTO;
    }

    private boolean invaildCarsystem(FordV2VinAnalyserResultDTO vinAnalyserResultDTO, CarTypeDetail carTypeDetail) {
        //客服反馈，单机版数据情况为：18年生产的解析不出。翼虎、锐界的编码不对。以及福克斯13款
        String carSystem = ignoreCarSystem.get(carTypeDetail.getName());
        if (StringUtils.isBlank(carSystem)) {
            return false;
        }
        boolean isfukesi = carSystem.contains("福克斯");
        if (isfukesi && vinAnalyserResultDTO.getDate().contains(V2Constant.IGNORE_FUKESIYEAR)) {
            log.warn("此vin为13款福克斯车系，单机版解析不准确，不进行解析:{}", vinAnalyserResultDTO.getVinCode());
            return true;
        }
        if (!isfukesi) {
            log.warn("此vin为翼虎或锐界车系，单机版解析不准确，不进行解析:{}", vinAnalyserResultDTO.getVinCode());
            return true;
        }
        return false;
    }

    @Override
    protected List<EPCCarTypeDTO> queryCarTypes(FordV2VinAnalyserResultDTO analyseResult) {
        String vinCode = analyseResult.getVinCode();
        boolean isCN = vinCode.startsWith("L");
        String mj = analyseResult.getDate().substring(0, 4);
        String optionCode = analyseResult.getDriven() + "_" + analyseResult.getEngine() + "_" + analyseResult.getVer() + "_" +
                analyseResult.getCarBody() + "_" + analyseResult.getTransmission() + "_" + analyseResult.getCarType();
        String btrId = carTypeDAO.queryBTRid(mj, optionCode, isCN);
        if (StringUtils.isBlank(btrId)) {
            log.warn("福特V2找不到关联的BTRID，请核查vin{}", vinCode);
            return Collections.emptyList();
        }

        EPCCarTypeDTO carType = new EPCCarTypeDTO();
        carType.setBtrId(btrId);
        carType.setVinCode(vinCode);

        String engineText = commonDAO.queryTextByIdx(analyseResult.getEngine());
        List<EPCCarTypeDTO.ExtendAttributeDTO> extendAttributeDTOS = Lists.newArrayList();
        extendAttributeDTOS.add(new EPCCarTypeDTO.ExtendAttributeDTO("engineDescription", engineText, "发动机描述"));
        extendAttributeDTOS.add(new EPCCarTypeDTO.ExtendAttributeDTO("yearRange", mj, "年款,匹配碰撞部位年款范围"));
        carType.setExtendAttrDTOs(extendAttributeDTOS);
        return Lists.newArrayList(carType);
    }

    @Override
    protected List<FordPartsMappingEntity> queryPartsMappings(Set<String> btrPartsNames, FordV2VinAnalyserResultDTO analyseResult) {
        if (CollectionUtils.isEmpty(btrPartsNames)) {
            return Collections.emptyList();
        }
        return partsMappingDAO.queryByPartsName(btrPartsNames);
    }

    @Override
    protected List<FordV2PartsInfoEntity> queryPartsInfoByPartsMapping(List<FordPartsMappingEntity> partsMappings, FordV2VinAnalyserResultDTO analyseResult) {
        if (CollectionUtils.isEmpty(partsMappings)) {
            return Collections.emptyList();
        }

        Set<String> partsCodes = partsMappings.stream().map(FordPartsMappingEntity::getPartsCode).collect(Collectors.toSet());
        return StreamKit.partitionStream(partsCodes, SEGMENT_QUERY_SIZE).flatMap(l ->
                queryPartsInfoByPartsCode(new HashSet<>(l), analyseResult).stream()
        ).collect(Collectors.toList());
    }

    @Override
    protected List<FordV2PartsInfoEntity> queryPartsInfoByPartsCode(Set<String> partsCodes) {
        return fordV2PartsInfoDAO.queryPartsInfoByPartsCode(partsCodes);
    }

    @Override
    protected List<FordV2PartsInfoEntity> queryPartsInfoByPartsCode(Set<String> partsCodes, FordV2VinAnalyserResultDTO analyseResult) {
        if (CollectionUtils.isEmpty(partsCodes)) {
            return Collections.emptyList();
        }
        return fordV2PartsInfoDAO.queryPartsInfoByPartsCodeAndSubGroup(partsCodes, analyseResult.getCarTypeDetail().getIdx());
    }

    @Override
    protected List<EPCPartsDTO> getEPCPartsDTOListFromBtrPartsByPartsName(Set<String> btrPartsNames, FordV2VinAnalyserResultDTO analyseResult) {

        if (CollectionUtils.isEmpty(btrPartsNames) || analyseResult == null) {
            return Collections.emptyList();
        }

        //非油类自编码
        Set<FordBtrPartsDTO> btrPartsInfo = queryBtrParts(btrPartsNames, analyseResult);

        Set<FordBtrPartsDTO> fordV2BtrPartsDTOS = fordBtrPartsDAO.queryOilBtrParts(btrPartsNames);
        if (fordV2BtrPartsDTOS.stream().anyMatch(Objects::isNull)) {
            log.warn("btrPartsInfoByOil: {}", fordV2BtrPartsDTOS);
        }
        Set<FordBtrPartsDTO> resultBtrPartsInfo = Sets.union(btrPartsInfo, fordV2BtrPartsDTOS);
        if (CollectionUtils.isEmpty(resultBtrPartsInfo)) {
            return Collections.emptyList();
        }
        return resultBtrPartsInfo.stream()
                .filter(Objects::nonNull)
                .map(p -> {
                    EPCPartsDTO epcPartsDTO = new EPCPartsDTO();
                    epcPartsDTO.setBtrPartsName(p.getBtrPartsName());
                    epcPartsDTO.setPartsCode(p.getPartsCode());
                    if (p.isHasImg()) {
                        EPCPartsImageDTO epcPartsImageDTO = new EPCPartsImageDTO();
                        epcPartsImageDTO.setImageName(p.getPartsCode());
                        String imageUrl = "oil/" + p.getPartsCode() + ".png";
                        String imgPath = appConfigLoader.getEPCPartsImageFullUrl(EPCAnalyserEnum.FORD, imageUrl);
                        epcPartsImageDTO.setImageUrl(imgPath);
                        epcPartsDTO.setEpcPartsImageDTOs(Lists.newArrayList(epcPartsImageDTO));
                    }
                    return epcPartsDTO;
                }).collect(Collectors.toList());
    }

    private Set<FordBtrPartsDTO> queryBtrParts(Set<String> btrPartsNames, FordV2VinAnalyserResultDTO analyseResult) {

        Set<FordBtrPartsDTO> gmBtrPartsDTOS = fordBtrPartsDAO.queryBtrPartsNameByBrand(btrPartsNames);

        Set<String> btrPartsName = gmBtrPartsDTOS.stream().map(FordBtrPartsDTO::getBasicPartsName).collect(Collectors.toSet());

        List<FordPartsMappingEntity> partsMappings = queryPartsMappingsForCall(btrPartsName, analyseResult);
        List<FordV2PartsInfoEntity> partsInfo = queryPartsInfoByPartsMappingForCall(partsMappings, analyseResult);
        partsInfo = filterPartsInfoByVinAnalyseResultForCall(partsInfo, analyseResult);
        Map<String, List<FordV2PartsInfoEntity>> partsInfoGroupByPartsName = getPartsInfoGroupByPartsNameForCall(partsInfo, partsMappings);
        partsInfoGroupByPartsName = filterPartsInfoByVinAnalyseResultForCall(partsInfoGroupByPartsName, analyseResult);
        List<EPCPartsDTO> epcPartsList = getEPCPartsDTOListForCall(partsInfoGroupByPartsName, analyseResult, null);
        if (CollectionUtils.isEmpty(epcPartsList)) {
            return Collections.emptySet();
        }

        Map<String, List<EPCPartsDTO>> partsMap = epcPartsList.stream().collect(Collectors.groupingBy(EPCPartsDTO::getBtrPartsName));
        return gmBtrPartsDTOS.stream()
                .filter(p -> CollectionUtils.isNotEmpty(partsMap.get(p.getBasicPartsName())))
                .flatMap(p -> {
                    List<EPCPartsDTO> epcPartsDTOS = partsMap.get(p.getBasicPartsName());
                    return epcPartsDTOS.stream().map(l ->
                            FordBtrPartsDTO.builder()
                                    .btrPartsName(p.getBtrPartsName())
                                    .partsCode(l.getPartsCode() + p.getDistinguishingMarks() + p.getSuffix())
                                    .build()
                    );
                }).collect(Collectors.toSet());
    }

    @Override
    protected List<FordV2PartsInfoEntity> filterPartsInfoByVinAnalyseResult(List<FordV2PartsInfoEntity> partsInfo, FordV2VinAnalyserResultDTO analyseResult) {
        List<String> mGroup = getMG(analyseResult);
        if (CollectionUtils.isEmpty(mGroup)) {
            return Collections.emptyList();
        }
        List<FordV2SGroupDTO> sGroup = getSG(analyseResult, mGroup);

        if (CollectionUtils.isEmpty(sGroup)) {
            return Collections.emptyList();
        }
        if (analyseResult.getLessInfo() != -1) {
            List<FordLessInfoLevelDTO> lessInfoRule = vinInfoDAO.queryLessInfoIdx(analyseResult.getLessInfo() - V2Constant.RULE_NUM, analyseResult.getLessInfo());
            if (CollectionUtils.isEmpty(lessInfoRule)) {
                return partsInfo;
            }
            List<String> condition = filterByLevel(lessInfoRule);
            partsInfo.removeIf(d -> filterLessInfo(d.getDealTextCondition3(), condition, analyseResult));
            sGroup.removeIf(d -> filterLessInfo(d.getCondition1(), condition, analyseResult));
        }
        partsInfo.removeIf(d -> matchGroup(d, sGroup));
        partsInfo.removeIf(d -> matchDate(d, analyseResult));

        partsInfo.removeIf(d -> filterCondition(d.getDealTextCondition3(), analyseResult));
        return partsInfo;
    }


    private List<String> filterByLevel(List<FordLessInfoLevelDTO> lessInfoRule) {
        Map<Integer, List<FordLessInfoLevelDTO>> lessInfoGroupByGrade = lessInfoRule.stream()
                .collect(Collectors.groupingBy(FordLessInfoLevelDTO::getGrade));

        return lessInfoGroupByGrade.values().stream()
                .map(d -> d.stream().max(Comparator.comparingInt(FordLessInfoLevelDTO::getVinIdx)).get())
                .filter(d -> d.getLessInfoIndex() != 0)
                .map(FordLessInfoLevelDTO::getLessInfo)
                .collect(Collectors.toList());
    }

    private boolean filterLessInfo(String condition, List<String> lessInfo, FordV2VinAnalyserResultDTO analyseResult) {
        if (!filterCondition(condition, analyseResult)) {
            return false;
        }
        List<String> c = Splitter.on("|").splitToList(condition);
        return c.stream().noneMatch(d -> filterLessInfoDetail(d, lessInfo, analyseResult));
    }

    private boolean filterLessInfoDetail(String subCondition, List<String> lessInfo, FordV2VinAnalyserResultDTO analyseResult) {
        List<String> detail = Splitter.on(conditionPattern).trimResults().splitToList(subCondition);
        return detail.stream().allMatch(d -> {
            if (StringUtils.isBlank(d)) {
                return true;
            }
            d = d.replace(" ", "0");
            List<String> con;
            String sub = StringUtils.length(d) == 6 ? StringUtils.substring(d, 0, 3) : StringUtils.substring(d, 0, 2);
            con = lessInfo.stream().filter(q -> Objects.nonNull(q) && q.startsWith(sub)).collect(Collectors.toList());
            return CollectionUtils.isEmpty(con) || con.contains(d) || con.contains(d.substring(0, d.length() - 1));
        });
    }

    private boolean matchDate(FordV2PartsInfoEntity partsInfoEntity, FordV2VinAnalyserResultDTO analyseResult) {
        String vinDate = analyseResult.getDate();
        String dealBegin = partsInfoEntity.getDealBegin();
        String dealEnd = partsInfoEntity.getDealEnd();
        return vinDate.compareTo(dealBegin) <= 0 || vinDate.compareTo(dealEnd) >= 0;
    }

    private boolean matchGroup(FordV2PartsInfoEntity partsInfo, List<FordV2SGroupDTO> sGroup) {
        String subGroup = partsInfo.getSubGroup();
//        String subGroupIdx = partsInfo.getSubGroupIdx();
        return sGroup.stream().noneMatch(d -> d.getSubGroup().contains(subGroup));
//                && StringUtils.equals(d.getSubGroupIdx(), subGroupIdx));
    }

    private List<FordV2SGroupDTO> getSG(FordV2VinAnalyserResultDTO analyseResult, List<String> mGroup) {
        String carTypeId = analyseResult.getCarTypeDetail().getIdx();
        List<FordV2SGroupDTO> fordV2SGroupDTOs = StreamKit.partitionStream(mGroup, 150)
                .flatMap(l -> groupDAO.querySGroup(carTypeId, l).stream()).collect(Collectors.toList());

        fordV2SGroupDTOs.removeIf(d -> filterCondition(d.getCondition1(), analyseResult));
        return fordV2SGroupDTOs;
    }

    private List<String> getMG(FordV2VinAnalyserResultDTO analyseResult) {
        String carTypeId = analyseResult.getCarTypeDetail().getIdx();
        List<FordV2MGroupDTO> fordV2MGroupDTOs = groupDAO.queryMGroupByIdex(carTypeId);
        fordV2MGroupDTOs.removeIf(d -> filterCondition(d.getCondition1(), analyseResult));
        List<String> mainGroup = fordV2MGroupDTOs.stream().map(FordV2MGroupDTO::getMGroup).collect(Collectors.toList());
        boolean haveAYH = mainGroup.stream().noneMatch(d -> d.contains("AYH"));
        boolean haveMFA = mainGroup.stream().noneMatch(d -> d.contains("MFA"));
        if (haveAYH || haveMFA) {
            return mainGroup;
        }
        String engineText = commonDAO.queryTextByIdx(analyseResult.getEngine());
        if (StringUtils.containsIgnoreCase(engineText, "Duratec HE")) {
            mainGroup.removeIf(d -> d.contains("MFA"));
        } else {
            mainGroup.removeIf(d -> d.contains("AYH"));
        }
        return mainGroup;
    }

    private boolean filterCondition(String condition, FordV2VinAnalyserResultDTO analyseResult) {
        if (StringUtils.isBlank(condition)) {
            return false;
        }
        List<String> c = Splitter.on("|").trimResults().splitToList(condition);
        if (c.stream().allMatch(d -> d.startsWith("EN"))) {
            String engine = analyseResult.getEngineDetail();
            List<String> engines = Splitter.on(",").splitToList(engine);
            if (engines.stream().noneMatch(c::contains)) {
                return false;
            }
        }
        return c.stream().noneMatch(d -> filterDetail(d, analyseResult));
    }

    private boolean filterDetail(String subCondition, FordV2VinAnalyserResultDTO analyseResult) {
        String vinDate = analyseResult.getDate();
        List<String> detail = Splitter.on(conditionPattern).trimResults().splitToList(subCondition);
        return detail.stream().allMatch(d -> {
            if (StringUtils.startsWith(d, "DB")) {
                String date = dealDate(d);
                return vinDate.compareTo(date) >= 0;
            }
            if (StringUtils.startsWith(d, "DE")) {
                String date = dealDate(d);
                return vinDate.compareTo(date) <= 0;
            }
            if (StringUtils.startsWith(d, "XS")) {
                String nuArea = commonDAO.queryTextByIdx(d);
                if (StringUtils.contains(nuArea, "(-)")) {
                    String areaText = commonDAO.queryTextByIdx(analyseResult.getArea() + "0");
                    Matcher matcher = areaPattern.matcher(areaText);
                    if (matcher.find()) {
                        String areas = matcher.group(0);
                        areas = areas.replace("\"", "");
                        List<String> areaList = Splitter.on("/").splitToList(areas);
                        return areaList.stream().noneMatch(nuArea::contains);
                    }
                }
            }
            if (StringUtils.length(d) > 5 || isNum.matcher(StringUtils.substring(d, 2, 3)).matches()) {
                return true;
            }
            List<String> mainCondition = analyseResult.getCondition();
            return mainCondition.contains(d);
        });
    }


    private String dealDate(String d) {
        String date = StringUtils.replacePattern(d, DATE_REX, "");
        List<String> dates = Splitter.fixedLength(2).splitToList(date);
        if (dates.size() == 4) {
            date = dates.get(2) + dates.get(3) + dates.get(1) + dates.get(0);
        }
        return date;
    }


    @Override
    public void afterPropertiesSet() {
        initIgnoreCarSystem();
        initBtrRule();
    }

    private void initBtrRule() {
        List<FordV2BtrRuleDTO> fordV2BtrRuleDTOS = fordV2PartsInfoDAO.queryBtrRule();
        btrPartsNameRule = fordV2BtrRuleDTOS.stream().collect(Collectors.toMap(FordV2BtrRuleDTO::getBtrPartsName, FordV2BtrRuleDTO::getBtrRule, (v1, v2) -> v1));
    }

    private void initIgnoreCarSystem() {
        List<Map<String, String>> ignoreCarSystemMap = vinInfoDAO.queryIgnoreCarSystem();
        ignoreCarSystem = ignoreCarSystemMap.stream().collect(Collectors.toMap(k -> k.get("name"), v -> v.get("carSystem"), (v1, v2) -> v1));
    }

    @Override
    protected Map<String, List<FordV2PartsInfoEntity>> filterPartsInfoByVinAnalyseResult(Map<String, List<FordV2PartsInfoEntity>> partsInfoGroupByPartsName, FordV2VinAnalyserResultDTO analyseResult) {
        if (MapUtils.isEmpty(partsInfoGroupByPartsName)) {
            return Collections.emptyMap();
        }
        Set<String> btrPartsNameSet = partsInfoGroupByPartsName.keySet();
        Set<String> btrpartsNameRuleSet = btrPartsNameRule.keySet();
        Set<String> intersection = Sets.intersection(btrPartsNameSet, btrpartsNameRuleSet);
        if (CollectionUtils.isEmpty(intersection)) {
            return partsInfoGroupByPartsName;
        }
        intersection.forEach(d -> {
            List<FordV2PartsInfoEntity> fordV2PartsInfoEntities = partsInfoGroupByPartsName.get(d);
            int partsSize = fordV2PartsInfoEntities.stream().map(FordV2PartsInfoEntity::getPartsCode).collect(Collectors.toSet()).size();
            if (partsSize > 1) {
                String rule = btrPartsNameRule.get(d);
                // "/"分割符表示包含
                if (rule.contains(V2Constant.CONTAIN_RULE)) {
                    List<String> containRule = Splitter.on(V2Constant.CONTAIN_RULE).omitEmptyStrings().splitToList(rule);
                    fordV2PartsInfoEntities.removeIf(parts -> containRule.stream().anyMatch(r -> StringUtils.containsIgnoreCase(parts.getPartsCode(), r)));
                }
                // ":"分隔符表示开头
                if (rule.contains(V2Constant.START_RULE)) {
                    List<String> startRule = Splitter.on(V2Constant.START_RULE).omitEmptyStrings().splitToList(rule);
                    fordV2PartsInfoEntities.removeIf(parts -> startRule.stream().anyMatch(r -> StringUtils.startsWithIgnoreCase(parts.getPartsCode(), r)));
                }
            }
        });

        return partsInfoGroupByPartsName;
    }

    @Override
    protected Map<String, List<EPCPartsImageDTO>> queryPartsImage(Map<String, List<FordV2PartsInfoEntity>> partsInfoGroup, FordV2VinAnalyserResultDTO analyseResult) {

        List<FordV2PartsInfoEntity> partsList = partsInfoGroup.values().stream()
                .filter(CollectionUtils::isNotEmpty).flatMap(Collection::stream)
                .filter(d -> StringUtils.isNoneBlank(d.getPnc(), d.getImageCode(), d.getSubGroup(), d.getCarTypeId()))
                .collect(Collectors.toList());

        if (CollectionUtils.isEmpty(partsList)) {
            return Collections.emptyMap();
        }

        List<FordV2ImagePointDTO> imagePointDTOs = StreamKit.partitionStream(partsList, 100).flatMap(l -> fordV2PartsInfoDAO.queryImagePointDTO(l).stream()).collect(Collectors.toList());
        List<FordV2ImageInfoDTO> imageInfoDTOs = StreamKit.partitionStream(partsList, 100).flatMap(l -> fordV2PartsInfoDAO.queryImageInfoDTO(l).stream()).collect(Collectors.toList());

        return partsInfoGroup.keySet().stream().collect(Collectors.toMap(o -> o, q -> {
            List<FordV2PartsInfoEntity> fordV2PartsInfoEntities = partsInfoGroup.get(q);
            if (CollectionUtils.isEmpty(fordV2PartsInfoEntities)) {
                return Collections.emptyList();
            }
            Set<EPCPartsImageDTO> epcPartsImageDTOS = Sets.newHashSet();

            fordV2PartsInfoEntities.forEach(d -> {
                List<FordV2ImageInfoDTO> imageInfoDTO = imageInfoDTOs.stream()
                        .filter(image -> d.getSubGroup().equals(image.getSubGroup())
                                && d.getCarTypeId().equals(image.getCarTypeId())
                                && d.getImageCode().equals(image.getImageCode()))
                        .collect(Collectors.toList());
                if (CollectionUtils.isEmpty(imageInfoDTO)) {
                    return;
                }
                FordV2ImageInfoDTO fordV2ImageDTO = imageInfoDTO.get(0);
                EPCPartsImageDTO epcPartsImageDTO = new EPCPartsImageDTO();
                epcPartsImageDTO.setImageName(fordV2ImageDTO.getImageName());
                epcPartsImageDTO.setImageUrl(fordV2ImageDTO.getUrl());
                epcPartsImageDTO.setImageCode(fordV2ImageDTO.getImageCode());

                epcPartsImageDTO.setEpcPartsImagePositionDTOs(buildPoint(d, imagePointDTOs));

                epcPartsImageDTOS.add(epcPartsImageDTO);
            });
            return Lists.newArrayList(epcPartsImageDTOS);
        }));
    }

    private List<EPCPartsImagePositionDTO> buildPoint(FordV2PartsInfoEntity v2PartsInfoEntity, List<FordV2ImagePointDTO> imagePointDTOs) {
        List<FordV2ImagePointDTO> imageInfoDTO = imagePointDTOs.stream()
                .filter(image -> v2PartsInfoEntity.getSubGroup().equals(image.getSubGroup())
                        && v2PartsInfoEntity.getCarTypeId().equals(image.getCarTypeId())
                        && v2PartsInfoEntity.getPnc().equals(image.getPnc()))
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(imageInfoDTO)) {
            return Collections.emptyList();
        }
        List<EPCPartsImagePositionDTO> result = Lists.newArrayList();
        imageInfoDTO.forEach(d -> {
            EPCPartsImagePositionDTO epcPartsImagePositionDTO = new EPCPartsImagePositionDTO();
            EPCImagePointDTO bottom = new EPCImagePointDTO();
            bottom.setX(d.getBottomRightX());
            bottom.setY(d.getBottomRightY());
            epcPartsImagePositionDTO.setBottomRight(bottom);
            EPCImagePointDTO top = new EPCImagePointDTO();
            top.setX(d.getTopLeftX());
            top.setY(d.getTopLeftY());
            epcPartsImagePositionDTO.setTopLeft(top);
            epcPartsImagePositionDTO.setPosition(v2PartsInfoEntity.getPnc());
            result.add(epcPartsImagePositionDTO);
        });
        return result;
    }
}
